package model

import (
	"fmt"
	"gitee.com/jikey/elk-blog/setting"
	"github.com/spf13/cast"
	"log"

	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/mysql"

	"time"
)

var Mysql *gorm.DB

type BaseModel struct {
	Id         int    `gorm:"primary_key;autoIncrement;column:id" json:"id,omitempty" form:"id"`
	CreateTime string `gorm:"column:create_time" json:"create_time,omitempty" form:"create_time"`
	UpdateTime string `gorm:"column:update_time" json:"update_time,omitempty" form:"update_time"`
}

// Setup initializes the database instance
func init() {
	var err error
	Mysql, err = gorm.Open(setting.Config.Database.Type, fmt.Sprintf("%s:%s@tcp(%s:%s)/%s?charset=utf8mb4",
		setting.Config.Database.User,
		setting.Config.Database.Password,
		setting.Config.Database.Host,
		setting.Config.Database.Port,
		setting.Config.Database.DbName))

	if err != nil {
		log.Fatalf("models.Setup err: %v", err)
	}

	gorm.DefaultTableNameHandler = func(db *gorm.DB, defaultTableName string) string {
		return setting.Config.Database.TablePrefix + defaultTableName
	}

	Mysql.SingularTable(true)
	Mysql.Callback().Create().Replace("gorm:update_time_stamp", updateTimeStampForCreateCallback)
	Mysql.Callback().Update().Replace("gorm:update_time_stamp", updateTimeStampForUpdateCallback)
	Mysql.Callback().Delete().Replace("gorm:delete", deleteCallback)
	Mysql.DB().SetMaxIdleConns(25)                 // 设置最大空闲连接数
	Mysql.DB().SetMaxOpenConns(100)                // 设置最大连接数
	Mysql.DB().SetConnMaxLifetime(5 * time.Minute) // 设置每个链接的过期时间

	// 启用Logger，显示详细日志
	Mysql.LogMode(true)
}

// CloseDB closes database connection (unnecessary)
func CloseDB() {
	defer func(Mysql *gorm.DB) {
		err := Mysql.Close()
		if err != nil {
			log.Fatal(err)
		}
	}(Mysql)
}

// updateTimeStampForCreateCallback will set `CreatedOn`, `ModifiedOn` when creating
func updateTimeStampForCreateCallback(scope *gorm.Scope) {
	if !scope.HasError() {
		// nowTime := time.Now().Unix()
		nowTime := time.Now().Format("2006-01-02 15:04:05")
		if createTimeField, ok := scope.FieldByName("CreateTime"); ok {
			if createTimeField.IsBlank {
				err := createTimeField.Set(nowTime)
				if err != nil {
					return
				}
			}
		}

		if modifyTimeField, ok := scope.FieldByName("UpdateTime"); ok {
			if modifyTimeField.IsBlank {
				err := modifyTimeField.Set(nowTime)
				if err != nil {
					return
				}
			}
		}
	}
}

// updateTimeStampForUpdateCallback will set `ModifiedOn` when updating
func updateTimeStampForUpdateCallback(scope *gorm.Scope) {
	if _, ok := scope.Get("gorm:update_column"); !ok {
		scope.SetColumn("UpdateTime", time.Now().Format("2006-01-02 15:04:05"))
	}
}

// deleteCallback will set `DeletedOn` where deleting
func deleteCallback(scope *gorm.Scope) {
	if !scope.HasError() {
		var extraOption string
		if str, ok := scope.Get("gorm:delete_option"); ok {
			extraOption = fmt.Sprint(str)
		}

		deletedOnField, hasDeletedOnField := scope.FieldByName("DeletedOn")

		if !scope.Search.Unscoped && hasDeletedOnField {
			scope.Raw(fmt.Sprintf(
				"UPDATE %v SET %v=%v%v%v",
				scope.QuotedTableName(),
				scope.Quote(deletedOnField.DBName),
				scope.AddToVars(time.Now().Format("2006-01-02 15:04:05")),
				addExtraSpaceIfExist(scope.CombinedConditionSql()),
				addExtraSpaceIfExist(extraOption),
			)).Exec()
		} else {
			scope.Raw(fmt.Sprintf(
				"DELETE FROM %v%v%v",
				scope.QuotedTableName(),
				addExtraSpaceIfExist(scope.CombinedConditionSql()),
				addExtraSpaceIfExist(extraOption),
			)).Exec()
		}
	}
}

// addExtraSpaceIfExist adds a separator
func addExtraSpaceIfExist(str string) string {
	if str != "" {
		return " " + str
	}
	return ""
}

func (v *BaseModel) BeforeCreate(scope *gorm.Scope) error {
	err := scope.SetColumn("create_time", time.Now().Format("2006-01-02 15:04:05"))

	if err != nil {
		return err
	}

	err2 := scope.SetColumn("update_time", time.Now().Format("2006-01-02 15:04:05"))

	if err != nil {
		return err2
	}

	return nil
}

func (v *BaseModel) BeforeUpdate(scope *gorm.Scope) error {
	err := scope.SetColumn("update_time", time.Now().Format("2006-01-02 15:04:05"))

	if err != nil {
		return err
	}

	return nil
}

// StringID 获取 ID 的字符串格式
func (v *BaseModel) StringID() string {
	return cast.ToString(v.Id)
}
